13. Warn the User About Unsaved Changes

Warn the User About Unsaved Changes

Warning the User

You may have noticed that hitting the “Up” button or back button leaves the EditorActivity, but doesn’t save your changes. You want to be explicit about saving changes to the database, so we will only save if the user hits the “Save” button.

That said, if a user could accidentally hit the “Up” or back when adding some edits. To avoid ignoring their work, we should pop-up a dialog to warn them that they are leaving the editor without saving their changes.

Here is a demo of what this looks like in the Contacts app. You can see this behavior if you enter a new contact’s info and then hit the up button without saving:

User Warning

warn the user

Warn the User About Unsaved Changes part 2

The Desired behavior

So in our app, the desired behavior will be to warn the user with a pop up dialog that says “Discard your changes and quit editing?”. The options are “Keep Editing” (Stay on the activity) or “Discard” (Finish activity).

Steps

Step 1: Listen for whether changes were made

The first step is to decide when to show the “Discard changes” dialog. This should only happen if the user changed one of the parts of the form. What you can do is create a boolean called mPetHasChanged, which will be true if the user updates part of the pet form.

private boolean mPetHasChanged = false;

You can check if changes were made by adding a OnTouchListener:

// OnTouchListener that listens for any user touches on a View, implying that they are modifying
// the view, and we change the mPetHasChanged boolean to true.

    private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
       @Override
       public boolean onTouch(View view, MotionEvent motionEvent) {
           mPetHasChanged = true;
           return false;
       }
    };

In onCreate:

mNameEditText.setOnTouchListener(mTouchListener);
mBreedEditText.setOnTouchListener(mTouchListener);
mWeightEditText.setOnTouchListener(mTouchListener);
mGenderSpinner.setOnTouchListener(mTouchListener);

Step 2: Make a method for creating a “Discard changes” dialog

Let’s make a method which will create the dialog below:

private void showUnsavedChangesDialog(
        DialogInterface.OnClickListener discardButtonClickListener) {
    // Create an AlertDialog.Builder and set the message, and click listeners
    // for the positive and negative buttons on the dialog.
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(R.string.unsaved_changes_dialog_msg);
    builder.setPositiveButton(R.string.discard, discardButtonClickListener);
    builder.setNegativeButton(R.string.keep_editing, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            // User clicked the "Keep editing" button, so dismiss the dialog
            // and continue editing the pet.
            if (dialog != null) {
                dialog.dismiss();
            }
        }
    });

    // Create and show the AlertDialog
    AlertDialog alertDialog = builder.create();
    alertDialog.show();
}

This code makes a AlertDialog using the AlertDialogBuilder. The method accepts a OnClickListener for the discard button. We do this because the behavior for clicking back or up is a little bit different.

Also note that there are a lot of R.string’s used, you can add these:

<!-- Dialog message when user is leaving editor but hasn't saved changes [CHAR LIMIT=NONE] -->
<string name="unsaved_changes_dialog_msg">Discard your changes and quit editing?</string>

<!-- Dialog button text for the option to discard a user's changes [CHAR LIMIT=20] -->
<string name="discard">Discard</string>

<!-- Dialog button text for the option to keep editing the current pet [CHAR LIMIT=20] -->
<string name="keep_editing">Keep Editing</string>

Step 3: Hook up the back button

Here is the code for the back button. You need to override the activity's normal “back button”. If the pet has changed, you make a discarded click listener that closes the current activity. Then you pass this listener to the showUnsavedChangesDialog method you just created.

@Override
public void onBackPressed() {
    // If the pet hasn't changed, continue with handling back button press
    if (!mPetHasChanged) {
        super.onBackPressed();
        return;
    }

    // Otherwise if there are unsaved changes, setup a dialog to warn the user.
    // Create a click listener to handle the user confirming that changes should be discarded.
    DialogInterface.OnClickListener discardButtonClickListener =
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // User clicked "Discard" button, close the current activity.
                    finish();
                }
            };

    // Show dialog that there are unsaved changes
    showUnsavedChangesDialog(discardButtonClickListener);
}

Step 4: Hook up the up button

Here is the code for the Up button. It’s found inside of the onOptionsItemSelected method. If the pet has changed, you make a discarded click listener that navigates up. Then you pass this listener to the showUnsavedChangesDialog method you just created.

case android.R.id.home:
    // If the pet hasn't changed, continue with navigating up to parent activity
    // which is the {@link CatalogActivity}.
    if (!mPetHasChanged) {
        NavUtils.navigateUpFromSameTask(EditorActivity.this);
        return true;
    }

    // Otherwise if there are unsaved changes, setup a dialog to warn the user.
    // Create a click listener to handle the user confirming that
    // changes should be discarded.
    DialogInterface.OnClickListener discardButtonClickListener =
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // User clicked "Discard" button, navigate to parent activity.
                    NavUtils.navigateUpFromSameTask(EditorActivity.this);
                }
            };

    // Show a dialog that notifies the user they have unsaved changes
    showUnsavedChangesDialog(discardButtonClickListener);
    return true;

Code Diff

To see the full set of changes made on this file, you can look at the code diff.

Commit 1.25 Warn user about losing unsaved changes

Helpful links:

To add behavior to when the back button is clicked, see http://stackoverflow.com/questions/18337536/android-overriding-onbackpressed " target="_blank">this StackOverflow post.
To add behavior when the “Up” button is clicked, see this article. You’ll need to add code to the case when the android.R.id.home button is clicked.

How to create an AlertDialog.